//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


//#include "stdafx.h"
#include "SearchList.h"
#include "muuli_wdr.h"

#include "wx/xrc/xmlres.h"
#include "wx/xrc/xh_all.h"
// This is a global variable for checking if packets are OK or not.
int packetcheck;

CSearchFile::CSearchFile(CFile* in_data,uint32 nSearchID){

	m_nSearchID = nSearchID;
	in_data->Read(&m_abyFileHash,16);
	in_data->Read(&clientip,4);
	in_data->Read(&clientport,2);
	uint32 tagcount;
	in_data->Read(&tagcount,4);
/*
For some reason, sometimes (on some certain search queries like 'crack' 'pgp'
'cdrwin' 'C#' 'windows' etc, tagcount returned a buggy (HUGE) value, causing endless loop
here. Therefore adding check to assure tagcount is >0 and <10 before entering this loop.
*/
//	printf("DEBUG: TagCount: %u IP: %d.%d.%d.%d PORT: %d\n",tagcount, clientip[0], clientip[1], clientip[2], clientip[3], clientport);
	if ((tagcount>0) && (tagcount<10)) {
//		printf("---- Packet OK ---- Tagcount: %u\n", tagcount);
		for (int i=0; i<tagcount; i++){
			CTag* toadd = new CTag(in_data);
			taglist.Add(toadd);
		}
		int iSize = 2;
		char* tempName = GetStrTagValue(FT_FILENAME);
		if( tempName != NULL && (strlen(tempName)>0) )
			iSize = (int)strlen(tempName)+1;
		m_pszFileName = new char[iSize];
		if (tempName == NULL)
			strcpy(m_pszFileName,"?");
		else
			strcpy(m_pszFileName,tempName);
		m_nFileSize = GetIntTagValue(FT_FILESIZE);
		packetcheck=TRUE;
	}
	else {
		packetcheck=FALSE;
//		printf("######## Buggy packet! ######## Tagcount:%u\n",tagcount);
	}
}

CSearchFile::~CSearchFile(){
	for (int i = 0; i != taglist.GetSize();i++)
		safe_delete(taglist[i]);
	taglist.RemoveAll();
	taglist.SetSize(0);
}

uint32 CSearchFile::GetIntTagValue(uint8 tagname){
	for (int i = 0; i != taglist.GetSize(); i++){
		if (taglist[i]->tag->specialtag == tagname)
			return taglist[i]->tag->intvalue;
	}
	return 0;
}
char* CSearchFile::GetStrTagValue(uint8 tagname){
	for (int i = 0; i != taglist.GetSize(); i++){
		if (taglist[i]->tag->specialtag == tagname)
			return taglist[i]->tag->stringvalue;
	}
	return 0;
}
uint32 CSearchFile::AddSources(uint32 count){
	for (int i = 0; i != taglist.GetSize(); i++){
		if (taglist[i]->tag->specialtag == FT_SOURCES){
			taglist[i]->tag->intvalue += count;
			return taglist[i]->tag->intvalue;
		}
	}
	return 0;
}


uint32 CSearchFile::GetSourceCount(){
	return GetIntTagValue(FT_SOURCES);
}

CSearchList::CSearchList(){
	outputwnd = 0;
}

CSearchList::~CSearchList(){
	Clear();
}

void CSearchList::Clear(){
	for(POSITION pos = list.GetHeadPosition(); pos != NULL; pos = list.GetHeadPosition()) {
		delete list.GetAt(pos);
		list.RemoveAt(pos);
	}
}

void CSearchList::RemoveResults( uint32 nSearchID){
	// this will not delete the item from the window, make sure your code does it if you call this
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CSearchFile* cur_file =	list.GetAt(pos2);
		if( cur_file->GetSearchID() == nSearchID ){
			list.RemoveAt(pos2);
			delete cur_file;
		}
	}
}

void CSearchList::ShowResults( uint32 nSearchID){
  //outputwnd->SetRedraw(false);
	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		if( ((CSearchFile*)list.GetAt(pos))->GetSearchID() == nSearchID ){
			outputwnd->AddResult(list.GetAt(pos));
		}
	}
	//outputwnd->SetRedraw(true);
}

// Ok, nobody knows why is this here, so disabling it until
// someone needs it.
/*
void CSearchList::RemoveResults( CSearchFile* todel ){
	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		if( (CSearchFile*)list.GetAt(pos) == todel ){
		  // this should also be routed to the selected page only
		  CMuleNotebook* nb=(CMuleNotebook*)theApp.emuledlg->searchwnd->FindWindowById(ID_NOTEBOOK);
		  if(nb->GetSelection()==-1)
		    return;
		  CSearchListCtrl* ctrl=(CSearchListCtrl*)nb->FindWindowById(ID_SEARCHLISTCTRL,nb->GetPage(nb->GetSelection()));
		  //theApp.emuledlg->searchwnd->searchlistctrl->RemoveResult( todel );
   		        ctrl->RemoveResult(todel);
			list.RemoveAt(pos);
			delete todel;
			return;
		}
	}
}
*/
void CSearchList::NewSearch(CSearchListCtrl* in_wnd, CString resTypes, uint16 nSearchID){
	if(in_wnd)
		outputwnd = in_wnd;

	resultType=resTypes;
	m_nCurrentSearch = nSearchID;
	myHashList="";
	
	foundFilesCount.SetAt(nSearchID,0);
}

uint16 CSearchList::ProcessSearchanswer(char* in_packet, uint32 size, CUpDownClient* Sender){
	CSafeMemFile* packet = new CSafeMemFile((BYTE*)in_packet,size,0);

	if(Sender) {
	  theApp.emuledlg->searchwnd->CreateNewTab(Sender->GetUserName(),(uint32)Sender);
	}
#if 0
	if (Sender)
		theApp.emuledlg->searchwnd.CreateNewTab(CString(Sender->GetUserName()),(uint32)Sender);
#endif
	uint32 results;
	packet->Read(&results,4);
	uint32 mySearchID=( (Sender != NULL)? (uint32)Sender : m_nCurrentSearch);
	foundFilesCount.SetAt(mySearchID,0);
	for (int i = 0; i != results; i++){
		CSearchFile* toadd = new CSearchFile(packet, mySearchID);
		if (packetcheck) { // Make sure the packet is legal.
			AddToList(toadd, (Sender != NULL) );
		} else {
//			printf("And it sure was a buggy packet!\n");
		}
	}

	packet->Close();
	delete packet;
	return GetResultCount();
}

uint16 CSearchList::ProcessUDPSearchanswer(char* in_packet, uint32 size){
	CSafeMemFile* packet = new CSafeMemFile((BYTE*)in_packet,size,0);
	CSearchFile* toadd = new CSearchFile(packet, m_nCurrentSearch);
	AddToList(toadd);
	packet->Close();
	delete packet;
	return GetResultCount();
}

bool CSearchList::AddToList(CSearchFile* toadd, bool bClientResponse){
  // locate the searchlistctrl responsible for this search
  // and set outputlist temporarily to point to the correct control
  CSearchListCtrl* oldlistctrl=outputwnd;
  CMuleNotebook* nb=(CMuleNotebook*)XRCCTRL(*(theApp.emuledlg->searchwnd),"ID_NOTEBOOK",CMuleNotebook);
  if(nb->GetPageCount()>0) {
    int tabCounter=-1;
    for(tabCounter=0;tabCounter<nb->GetPageCount();tabCounter++) {
      if(nb->GetUserData(tabCounter)==toadd->GetSearchID())
	break;
    }
    if(tabCounter>=0) {
      outputwnd=(CSearchListCtrl*)(theApp.emuledlg->searchwnd->FindWindowById(ID_SEARCHLISTCTRL,nb->GetPage(tabCounter)));
    }
  }

  if (!bClientResponse && !(resultType == GetResString(IDS_SEARCH_ANY) || GetFiletypeByName(toadd->GetFileName())==resultType)){
    delete toadd;
    outputwnd=oldlistctrl;
    return false;
  }
	for (POSITION pos = list.GetHeadPosition(); pos != NULL; list.GetNext(pos)){
		CSearchFile* cur_file = list.GetAt(pos);
		if ( (!memcmp(toadd->GetFileHash(),cur_file->GetFileHash(),16)) && cur_file->GetSearchID() ==  toadd->GetSearchID()){
			cur_file->AddSources(toadd->GetIntTagValue(FT_SOURCES));
			if (outputwnd)
				outputwnd->UpdateSources(cur_file);
			delete toadd;
			outputwnd=oldlistctrl;
			return true;
		}
	}
	
	if (list.AddTail(toadd)) {	
		uint16 tempValue;
		foundFilesCount.Lookup(toadd->GetSearchID(),tempValue);
		foundFilesCount.SetAt(toadd->GetSearchID(),tempValue+1);
	}
	if (outputwnd)
		outputwnd->AddResult(toadd);
	outputwnd=oldlistctrl;
	return true;
}

uint16 CSearchList::GetResultCount(uint32 nSearchID) {
	uint16 hits = 0;
	for (POSITION pos = list.GetHeadPosition(); pos != NULL; list.GetNext(pos)){
		if( list.GetAt(pos)->GetSearchID() == nSearchID )
			hits += list.GetAt(pos)->GetIntTagValue(FT_SOURCES);
	}
	return hits;
}


uint16 CSearchList::GetResultCount(){
	return GetResultCount(m_nCurrentSearch);
}

uint16 CSearchList::GetFoundFiles(uint32 searchID) {
	uint16 returnVal;
	foundFilesCount.Lookup(searchID,returnVal);
	return returnVal;
}
